import { VisualizationEvent } from "../components/FeatureProcessor";

export class BarLayer {
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;
  private isPlaying: boolean = false;
  private animationFrameId: number | null = null;
  private barCount: number = 128; // Number of bars in the visualization
  private barData: number[] = new Array(128).fill(0); // Current bar heights
  private smoothedData: number[] = new Array(128).fill(0); // Smoothed bar heights

  constructor(canvas: HTMLCanvasElement) {
    this.canvas = canvas;
    const ctx = canvas.getContext('2d');
    if (!ctx) {
      throw new Error('Unable to get 2D context');
    }
    this.ctx = ctx;
  }

  public update(event: VisualizationEvent): void {
    if (!this.isPlaying) return;

    // Update bar data based on spectral data
    const centerX = this.canvas.width / 2;
    const centerY = this.canvas.height / 2;
    
    // Convert linear bar data to circular arrangement
    this.draw(centerX, centerY, event);
  }

  private draw(centerX: number, centerY: number, event: VisualizationEvent): void {
    // Do not clear canvas to preserve other layers - only draw the bars
    
    const radius = Math.min(this.canvas.width, this.canvas.height) * 0.15; // Radius of the circle
    const barWidth = (Math.PI * 2) / this.barCount; // Width of each bar in radians
    
    // Update bar data with new values from multiple features
    for (let i = 0; i < this.barCount; i++) {
      // Use multiple features with position-based variation
      const positionFactor = Math.sin(i * 0.2) * 0.5 + 0.5; // Position-based variation to avoid uniformity
      
      // Normalize and amplify different features
      const normalizedRMS = Math.min(2, event.rms * 20000); // Amplify RMS
      const normalizedCentroid = Math.min(2, event.spectralCentroid / 500); // Amplify spectral centroid
      const normalizedRolloff = Math.min(2, event.spectralRolloff / 1000); // Amplify spectral rolloff
      const normalizedBandwidth = Math.min(2, event.spectralBandwidth / 200); // Amplify spectral bandwidth
      const normalizedZeroCrossing = Math.min(2, event.zeroCrossingRate * 100); // Amplify zero crossing rate
      
      // Combine multiple features for richer visualization
      const combinedValue = (
        normalizedRMS * 0.3 + 
        normalizedCentroid * 0.2 + 
        normalizedRolloff * 0.2 + 
        normalizedBandwidth * 0.15 + 
        normalizedZeroCrossing * 0.15
      ) * positionFactor;
      
      // Apply smoothing to make animation less jittery but still responsive
      this.barData[i] = this.barData[i] * 0.5 + combinedValue * 0.5; // Balanced smoothing
    }
    
    // Draw bars in a circle
    this.ctx.save();
    this.ctx.translate(centerX, centerY);
    
    for (let i = 0; i < this.barCount; i++) {
      const angle = i * barWidth - Math.PI / 2; // Start from top (-90 degrees)
      
      // Apply smoothing to the bars
      this.smoothedData[i] = this.smoothedData[i] * 0.3 + this.barData[i] * 0.7; // Balanced smoothing
      
      // Calculate bar height based on combined feature data
      // Base height of 5 + variable height up to 65 for more visual impact
      let barHeight = 5 + this.smoothedData[i] * 30; // Scale the height
      
      // Add some variation based on MFCCs
      if (event.mfccs.length > 0) {
        const mfccValue = Math.abs(event.mfccs[0] || 0);
        barHeight += mfccValue * 5;
      }
      
      barHeight = Math.min(65, Math.max(5, barHeight)); // Cap between 5 and 65
      
      // Create gradient for the bar
      const gradient = this.ctx.createLinearGradient(
        0, 0, 
        Math.cos(angle) * barHeight, 
        Math.sin(angle) * barHeight
      );
      
      // Color based on position and features
      const hue = (i * 360 / this.barCount) % 360;
      const energyHue = (hue + event.spectralCentroid / 30) % 360;
      
      gradient.addColorStop(0, `hsla(${energyHue}, 80%, 60%, 0.9)`);
      gradient.addColorStop(1, `hsla(${(energyHue + 60) % 360}, 90%, 50%, 0.8)`);
      
      // Draw the bar
      this.ctx.beginPath();
      this.ctx.moveTo(
        Math.cos(angle) * radius,
        Math.sin(angle) * radius
      );
      this.ctx.lineTo(
        Math.cos(angle) * (radius + barHeight),
        Math.sin(angle) * (radius + barHeight)
      );
      
      this.ctx.strokeStyle = gradient;
      // Variable line width based on energy for more visual impact
      const lineWidth = Math.min(8, Math.max(1, barWidth * radius * (0.3 + this.smoothedData[i] * 0.7)));
      this.ctx.lineWidth = lineWidth;
      this.ctx.lineCap = 'round';
      this.ctx.shadowBlur = Math.min(20, this.smoothedData[i] * 15); // Add glow effect based on energy
      this.ctx.shadowColor = `hsla(${energyHue}, 80%, 50%, 0.6)`;
      this.ctx.stroke();
      this.ctx.shadowBlur = 0; // Reset shadow
    }
    
    this.ctx.restore();
  }

  public play(): void {
    this.isPlaying = true;
    this.animate();
  }

  public pause(): void {
    this.isPlaying = false;
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId);
      this.animationFrameId = null;
    }
  }

  private animate(): void {
    if (!this.isPlaying) return;
    
    // We don't draw directly here since we need event data
    // Drawing happens in the update method
    this.animationFrameId = requestAnimationFrame(() => this.animate());
  }

  public resize(width: number, height: number): void {
    this.canvas.width = width;
    this.canvas.height = height;
  }
}